package app

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"gin-demo/pkg/e"
	"gin-demo/pkg/logging"
	"github.com/astaxie/beego/validation"
	"github.com/gin-gonic/gin"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"net/url"
	"strings"
	"time"
)

// curl
const (
	RequestTimeout time.Duration = time.Second * 50
)

type ResponseJson struct {
	Code int         `json:"code,omitempty"` // code
	Msg  Msg         `json:"msg,omitempty"`  // message
	Data interface{} `json:"data,omitempty"` // data
}

type Msg struct {
	Code   int    `json:"code,omitempty"`   // code
	Detail string `json:"detail,omitempty"` // detail
}

// MarkErrors logs error logs
func MarkErrors(errors []*validation.Error) {
	for _, err := range errors {
		logging.Info(err.Key, err.Message)
	}

	return
}

func ErrorsToMap(errors []*validation.Error) map[string]string {
	result := make(map[string]string)
	for _, err := range errors {
		fmt.Println(fmt.Sprintf("参数错误：%v", err))
		//logging.Info(err.Field, err.Message)
		result[err.Field] = err.Message
	}
	return result
}

func BindPostBody(ctx *gin.Context, postData interface{}) (err error) {

	if err = ctx.Bind(postData); err != nil {
		err = NewResponseErr(http.StatusBadRequest, e.InvalidParams, nil)
		return
	}
	valid := validation.Validation{}
	if ok, _ := valid.Valid(postData); !ok {
		_ = ErrorsToMap(valid.Errors)
		err = NewResponseErr(http.StatusBadRequest, e.InvalidParams, nil)
		return
	}
	return
}

func RequestPost(method, url string, data url.Values, token string) (*ResponseJson, error) {

	body := ioutil.NopCloser(strings.NewReader(data.Encode())) // 把form数据编下码
	client := &http.Client{}
	defer client.CloseIdleConnections()

	// 请求
	request, err := http.NewRequest(method, url, body)
	if err != nil {
		return nil, err
	}

	// 超时
	ctx, cancel := context.WithTimeout(context.Background(), RequestTimeout)
	defer cancel()
	request.WithContext(ctx)

	//给一个key设定为响应的value.
	request.Header.Set("Content-Type", "application/x-www-form-urlencoded;param=value") //必须设定该参数,POST参数才能正常提交
	if token != "" {
		request.Header.Set("Authorization", token)
	}
	resp, err := client.Do(request) //发送请求
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close() //一定要关闭resp.Body

	if resp.StatusCode >= 500 {
		return nil, errors.New("server error")
	}

	content, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	var resData ResponseJson
	if err := json.Unmarshal([]byte(content), &resData); err != nil {
		return nil, err
	}
	return &resData, nil
}

func RequestGet(urlStr string, params url.Values) (*ResponseJson, error) {

	Url, err := url.Parse(urlStr)
	if err != nil {
		return nil, err
	}
	//如果参数中有中文参数,这个方法会进行URLEncode
	Url.RawQuery = params.Encode()
	urlPath := Url.String()

	client := &http.Client{}
	defer client.CloseIdleConnections()

	// http
	httpReq, err := http.NewRequest(http.MethodGet, urlPath, nil)
	if err != nil {
		return nil, err
	}

	// 超时
	ctx, cancel := context.WithTimeout(context.Background(), RequestTimeout)
	defer cancel()
	httpReq.WithContext(ctx)

	//resp, err := http.Get(urlPath)
	resp, err := client.Do(httpReq)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 500 {
		return nil, errors.New("server error")
	}
	content, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	var resData ResponseJson
	if err := json.Unmarshal([]byte(content), &resData); err != nil {
		return nil, err
	}
	return &resData, nil
}

// UploadFileRequest .
// post file
func UploadFileRequest(uri string, params map[string]string, name string, fileData *[]byte, token string) (*ResponseJson, error) {

	client := &http.Client{}
	defer client.CloseIdleConnections()

	body := new(bytes.Buffer)
	writer := multipart.NewWriter(body)
	part, err := writer.CreateFormFile(name, name)
	if err != nil {
		return nil, err
	}
	if _, err := part.Write(*fileData); err != nil {
		return nil, err
	}

	for key, val := range params {
		_ = writer.WriteField(key, val)
	}
	err = writer.Close()
	if err != nil {
		return nil, err
	}
	request, err := http.NewRequest("POST", uri, body)
	if err != nil {
		return nil, err
	}

	// 超时
	ctx, cancel := context.WithTimeout(context.Background(), RequestTimeout)
	defer cancel()
	request.WithContext(ctx)

	//给一个key设定为响应的value.
	request.Header.Add("Content-Type", writer.FormDataContentType()) //必须设定该参数,POST参数才能正常提交
	if token != "" {
		request.Header.Set("Authorization", token)
	}
	resp, err := client.Do(request) //发送请求
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close() //一定要关闭resp.Body

	content, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode < 500 {
		var resData ResponseJson
		if err := json.Unmarshal(content, &resData); err != nil {
			return nil, err
		}
		return &resData, nil
	} else {
		return nil, errors.New(fmt.Sprintf("upload file client error: %s", content))
	}
}
